import React, { useEffect, useRef, useState } from "react"; import PlayerComponent from "@/components/watch/player/playerComponent"; import { FlagIcon, ShareIcon } from "@heroicons/react/24/solid"; import Details from "@/components/watch/primary/details"; import EpisodeLists from "@/components/watch/secondary/episodeLists"; import { getServerSession } from "next-auth"; import { useWatchProvider } from "@/lib/context/watchPageProvider"; import { authOptions } from "../../../api/auth/[...nextauth]"; import { createList, createUser, getEpisode } from "@/prisma/user"; import Link from "next/link"; import MobileNav from "@/components/shared/MobileNav"; import { NewNavbar } from "@/components/shared/NavBar"; import Modal from "@/components/modal"; import AniList from "@/components/media/aniList"; import { signIn } from "next-auth/react"; import BugReportForm from "@/components/shared/bugReport"; import Skeleton from "react-loading-skeleton"; import Head from "next/head"; export async function getServerSideProps(context) { let userData = null; const session = await getServerSession(context.req, context.res, authOptions); const accessToken = session?.user?.token || null; const query = context?.query; if (!query) { return { notFound: true, }; } let proxy; proxy = process.env.PROXY_URI; if (proxy.endsWith("/")) { proxy = proxy.slice(0, -1); } const disqus = process.env.DISQUS_SHORTNAME; const [aniId, provider] = query?.info; const watchId = query?.id; const epiNumber = query?.num; const dub = query?.dub; const ress = await fetch(`https://graphql.anilist.co`, { method: "POST", headers: { "Content-Type": "application/json", ...(accessToken && { Authorization: `Bearer ${accessToken}` }), }, body: JSON.stringify({ query: `query ($id: Int) { Media (id: $id) { mediaListEntry { progress status customLists repeat } id idMal title { romaji english native } status genres episodes studios { edges { node { id name } } } bannerImage description coverImage { extraLarge color } synonyms } } `, variables: { id: aniId, }, }), }); const data = await ress.json(); try { if (session) { await createUser(session.user.name); await createList(session.user.name, watchId); const data = await getEpisode(session.user.name, watchId); userData = JSON.parse( JSON.stringify(data, (key, value) => { if (key === "createdDate") { return String(value); } return value; }) ); } } catch (error) { console.error(error); // Handle the error here } return { props: { sessions: session, provider: provider || null, watchId: watchId || null, epiNumber: epiNumber || null, dub: dub || null, userData: userData?.[0] || null, info: data?.data?.Media || null, proxy, disqus, }, }; } export default function Watch({ info, watchId, disqus, proxy, dub, userData, sessions, provider, epiNumber, }) { const [artStorage, setArtStorage] = useState(null); const [episodeNavigation, setEpisodeNavigation] = useState(null); const [episodesList, setepisodesList] = useState(); const [mapEpisode, setMapEpisode] = useState(null); const [episodeSource, setEpisodeSource] = useState(null); const [open, setOpen] = useState(false); const [isOpen, setIsOpen] = useState(false); const [onList, setOnList] = useState(false); const { theaterMode, setPlayerState, setAutoPlay, setMarked } = useWatchProvider(); const playerRef = useRef(null); useEffect(() => { async function getInfo() { if (info.mediaListEntry) { setOnList(true); } const response = await fetch( `/api/v2/episode/${info.id}?releasing=${ info.status === "RELEASING" ? "true" : "false" }${dub ? "&dub=true" : ""}` ).then((res) => res.json()); const getMap = response.find((i) => i?.map === true) || response[0]; let episodes = response; if (getMap) { if (provider === "gogoanime" && !watchId.startsWith("/")) { episodes = episodes.filter((i) => { if (i?.providerId === "gogoanime" && i?.map !== true) { return null; } return i; }); } setMapEpisode(getMap?.episodes); } if (episodes) { const getProvider = episodes?.find((i) => i.providerId === provider); const episodeList = getProvider?.episodes.slice( 0, getMap?.episodes.length ); const playingData = getMap?.episodes.find( (i) => i.number === Number(epiNumber) ); if (getProvider) { setepisodesList(episodeList); const currentEpisode = episodeList?.find( (i) => i.number === parseInt(epiNumber) ); const nextEpisode = episodeList?.find( (i) => i.number === parseInt(epiNumber) + 1 ); const previousEpisode = episodeList?.find( (i) => i.number === parseInt(epiNumber) - 1 ); setEpisodeNavigation({ prev: previousEpisode, playing: { id: currentEpisode.id, title: playingData?.title, description: playingData?.description, img: playingData?.img || playingData?.image, number: currentEpisode.number, }, next: nextEpisode, }); } } setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings"))); // setEpiData(episodes); } getInfo(); return () => { setEpisodeNavigation(null); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [sessions?.user?.name, epiNumber, dub]); useEffect(() => { async function fetchData() { if (info) { const autoplay = localStorage.getItem("autoplay_video") === "true" ? true : false; setAutoPlay(autoplay); const anify = await fetch("/api/v2/source", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ source: provider === "gogoanime" && !watchId.startsWith("/") ? "consumet" : "anify", providerId: provider, watchId: watchId, episode: epiNumber, id: info.id, sub: dub ? "dub" : "sub", }), }).then((res) => res.json()); const skip = await fetch( `https://api.aniskip.com/v2/skip-times/${info.idMal}/${parseInt( epiNumber )}?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=` ).then((res) => { if (!res.ok) { switch (res.status) { case 404: { return null; } } } return res.json(); }); const op = skip?.results?.find((item) => item.skipType === "op") || null; const ed = skip?.results?.find((item) => item.skipType === "ed") || null; const episode = { epiData: anify, skip: { op, ed, }, }; setEpisodeSource(episode); } } fetchData(); return () => { setEpisodeSource(); setPlayerState({ currentTime: 0, isPlaying: false, }); setMarked(0); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [provider, watchId, info?.id]); useEffect(() => { const mediaSession = navigator.mediaSession; if (!mediaSession) return; const now = episodeNavigation?.playing; const poster = now?.img || info?.bannerImage; const title = now?.title || info?.title?.romaji; const artwork = poster ? [{ src: poster, sizes: "512x512", type: "image/jpeg" }] : undefined; mediaSession.metadata = new MediaMetadata({ title: title, artist: `Moopa ${ title === info?.title?.romaji ? "- Episode " + epiNumber : `- ${info?.title?.romaji || info?.title?.english}` }`, artwork, }); }, [episodeNavigation, info, epiNumber]); const handleShareClick = async () => { try { if (navigator.share) { await navigator.share({ title: `Watch Now - ${info?.title?.english || info.title.romaji}`, // text: `Watch [${info?.title?.romaji}] and more on Moopa. Join us for endless anime entertainment"`, url: window.location.href, }); } else { // Web Share API is not supported, provide a fallback or show a message alert("Web Share API is not supported in this browser."); } } catch (error) { console.error("Error sharing:", error); } }; function handleOpen() { setOpen(true); document.body.style.overflow = "hidden"; } function handleClose() { setOpen(false); document.body.style.overflow = "auto"; } return ( <> {episodeNavigation?.playing?.title || `${info?.title?.romaji} - Episode ${epiNumber}`} {/* Write the best SEO for this watch page with data of anime title from info.title.romaji, episode title from episodeNavigation?.playing?.title, description from episodeNavigation?.playing?.description, episode number from epiNumber */} {/* Write the best SEO for this homepage */} handleClose()}> {!sessions && (

Edit your list

)}
{theaterMode && ( )}
{!theaterMode && ( )}
{(episodeNavigation?.playing?.title || info.title.romaji) ?? "Loading..."}

{episodeNavigation?.playing?.number ? ( `Episode ${episodeNavigation?.playing?.number}` ) : ( )}

{/*
right
*/}
handleOpen()} disqus={disqus} />
); }